#include "DmMainAfx.h"

#include "xGraphic_Type.h"
#include "AlphaMixImp.h"

#include "BitBltImpl.h"
#include "StretchBltImpl.h"

#include "xGraphic_Init.h"
BOOL xRender::AlphaMix(PBITMAP lpbDst, xARGB clr, BYTE alpha)
{
    if (alpha == 0x00)
    {
        return TRUE;
    }
    else if (alpha == 0xFF)
    {
        return BlendSrcColor(lpbDst, clr);
    }

    LPBYTE lpScanDst = (LPBYTE)lpbDst->bmBits;

    WORD SrcConstantAlpha = alpha + 1;
    WORD DstConstantAlpha = 256 - SrcConstantAlpha;

    xARGB src = clr;
    clr.alpha = (clr.alpha * SrcConstantAlpha) >> 8;
    clr.red = (clr.red * SrcConstantAlpha) >> 8;
    clr.green = (clr.green * SrcConstantAlpha) >> 8;
    clr.blue = (clr.blue * SrcConstantAlpha) >> 8;

    for (int j = lpbDst->bmHeight; j > 0; j--)
    {
        LPXARGB lpPixelDst = (LPXARGB)lpScanDst;

        for (int i = lpbDst->bmWidth; i > 0; i--)
        {
            lpPixelDst->alpha = ((lpPixelDst->alpha * DstConstantAlpha) >> 8) + src.alpha;
            lpPixelDst->red   = ((lpPixelDst->red   * DstConstantAlpha) >> 8) + src.red;
            lpPixelDst->green = ((lpPixelDst->green * DstConstantAlpha) >> 8) + src.green;
            lpPixelDst->blue  = ((lpPixelDst->blue  * DstConstantAlpha) >> 8) + src.blue;

            lpPixelDst++;
        }

        lpScanDst += lpbDst->bmWidthBytes;
    }

    return TRUE;
}

BOOL xRender::AlphaMixMMX(PBITMAP lpbDst, xARGB clr, BYTE alpha)
{
    if (alpha == 0x00)
    {
        return TRUE;
    }
    else if (alpha == 0xFF)
    {
        return BlendSrcColor(lpbDst, clr);
    }

    LPDWORD lpScanDst = (LPDWORD)lpbDst->bmBits;
    DWORD dstWidth = lpbDst->bmWidth;

    WORD SrcConstantAlpha = alpha + 1;
    WORD DstConstantAlpha = 256 - SrcConstantAlpha;

    __asm
    {
        pxor mm7, mm7
        movd mm6, SrcConstantAlpha
        punpcklwd mm6, mm6
        punpckldq mm6, mm6
        movd mm5, DstConstantAlpha
        punpcklwd mm5, mm5
        punpckldq mm5, mm5
        movd mm2, clr.argb
        punpcklbw mm2, mm7
        pmullw mm2, mm6
        psrlw mm2, 8
        packuswb mm2, mm7
        punpckldq mm2, mm2
    }

    for (int j = 0; j < lpbDst->bmHeight; j++)
    {
        __asm
        {
            mov edi, lpScanDst
            mov ecx, dstWidth
            mov ebx, ecx
            xor eax, eax
            and ecx, (not 1)
            je ENDLOOP
            lea edi, [edi+ecx*4]
            neg ecx
WRITELOOP:
            movq mm0, [edi+ecx*4]
            movq mm1, mm0
            punpcklbw mm0, mm7
            punpckhbw mm1, mm7
            pmullw mm0, mm5
            pmullw mm1, mm5
            psrlw mm0, 8
            psrlw mm1, 8
            packuswb mm0, mm7
            packuswb mm1, mm7
            psllq mm1, 32
            pxor mm0, mm1
            paddusb	mm0, mm2
            movq qword ptr [edi+ecx*4], mm0
            add ecx, 2
            jnz WRITELOOP
ENDLOOP:
            mov ecx, ebx
            and ecx, 1
            je END
            movd mm0, [edi]
            punpcklbw mm0, mm7
            pmullw mm0, mm5
            psrlw mm0, 8
            packuswb mm0, mm7
            paddusb mm0, mm2
            movd [edi], mm0
END:
        }
        (LPBYTE&)lpScanDst += lpbDst->bmWidthBytes;
    }
    __asm
    {
        emms
    }
    return TRUE;
}

BOOL xRender::AlphaMix(PBITMAP lpbDst, PBITMAP lpbSrc, BYTE alpha)
{
    if (alpha == 0x00)
    {
        return TRUE;
    }
    else if (alpha == 0xFF)
    {
        BITMAPEX bmpSrc;
        memcpy(&bmpSrc, lpbSrc, sizeof(bmpSrc));
        bmpSrc.bmClrUsed = 0;
        bmpSrc.bmColors = NULL;
        return BlendSrc[BITS_32](lpbDst, &bmpSrc);
    }

    LPBYTE lpScanSrc = (LPBYTE)lpbSrc->bmBits;
    LPBYTE lpScanDst = (LPBYTE)lpbDst->bmBits;

    WORD SrcConstantAlpha = alpha + 1;
    WORD DstConstantAlpha = 256 - SrcConstantAlpha;

    for (int j = lpbDst->bmHeight; j > 0; j--)
    {
        LPXARGB lpPixelSrc = (LPXARGB)lpScanSrc;
        LPXARGB lpPixelDst = (LPXARGB)lpScanDst;

        for (int i = lpbDst->bmWidth; i > 0; i--)
        {
            xARGB src = *lpPixelSrc;

            lpPixelDst->alpha = (lpPixelDst->alpha * DstConstantAlpha + src.alpha * SrcConstantAlpha) >> 8;
            lpPixelDst->red   = (lpPixelDst->red   * DstConstantAlpha + src.red   * SrcConstantAlpha) >> 8;
            lpPixelDst->green = (lpPixelDst->green * DstConstantAlpha + src.green * SrcConstantAlpha) >> 8;
            lpPixelDst->blue  = (lpPixelDst->blue  * DstConstantAlpha + src.blue  * SrcConstantAlpha) >> 8;

            lpPixelSrc++;
            lpPixelDst++;
        }

        lpScanSrc += lpbSrc->bmWidthBytes;
        lpScanDst += lpbDst->bmWidthBytes;
    }

    return TRUE;
}

BOOL xRender::AlphaMixMMX(PBITMAP lpbDst, PBITMAP lpbSrc, BYTE alpha)
{
    if (alpha == 0x00)
    {
        return TRUE;
    }
    else if (alpha == 0xFF)
    {
        BITMAPEX bmpSrc;
        memcpy(&bmpSrc, lpbSrc, sizeof(bmpSrc));
        bmpSrc.bmClrUsed = 0;
        bmpSrc.bmColors = NULL;
        return BlendSrc[BITS_32](lpbDst, &bmpSrc);
    }

    LPDWORD lpScanSrc = (LPDWORD)lpbSrc->bmBits;
    LPDWORD lpScanDst = (LPDWORD)lpbDst->bmBits;
    DWORD dstWidth = lpbDst->bmWidth;

    WORD SrcConstantAlpha = alpha + 1;
    WORD DstConstantAlpha = 256 - SrcConstantAlpha;

    __asm
    {
        pxor mm7, mm7
        movd mm6, SrcConstantAlpha
        punpcklwd mm6, mm6
        punpckldq mm6, mm6
        movd mm5, DstConstantAlpha
        punpcklwd mm5, mm5
        punpckldq mm5, mm5
    }

    for (int j = 0; j < lpbDst->bmHeight; j++)
    {
        __asm
        {
            mov esi, lpScanSrc
            mov edi, lpScanDst
            mov ecx, dstWidth
            mov ebx, ecx
            xor eax, eax
            and ecx, (not 1)
            je ENDLOOP
            lea edi, [edi+ecx*4]
            lea esi, [esi+ecx*4]
            neg ecx
WRITELOOP:
            movq mm2, [esi+ecx*4]
            movq mm0, [edi+ecx*4]
            movq mm1, mm0
            movq mm3, mm2
            punpcklbw mm0, mm7
            punpckhbw mm1, mm7
            punpcklbw mm2, mm7
            punpckhbw mm3, mm7
            pmullw mm0, mm5
            pmullw mm2, mm6
            pmullw mm1, mm5
            pmullw mm3, mm6
            psrlw mm0, 8
            psrlw mm1, 8
            psrlw mm2, 8
            psrlw mm3, 8
            packuswb mm0, mm7
            packuswb mm1, mm7
            packuswb mm2, mm7
            packuswb mm3, mm7
            psllq mm1, 32
            psllq mm3, 32
            pxor mm0, mm1
            pxor mm2, mm3
            paddusb	mm0, mm2
            movq qword ptr [edi+ecx*4], mm0
            add ecx, 2
            jnz WRITELOOP
ENDLOOP:
            mov ecx, ebx
            and ecx, 1
            je END
            movd mm0, [edi]
            movd mm1, [esi]
            punpcklbw mm0, mm7
            punpcklbw mm1, mm7
            pmullw mm0, mm5
            pmullw mm1, mm6
            psrlw mm0, 8
            psrlw mm1, 8
            packuswb mm0, mm7
            packuswb mm1, mm7
            paddusb mm0, mm1
            movd [edi], mm0
END:
        }
        (LPBYTE&)lpScanSrc += lpbSrc->bmWidthBytes;
        (LPBYTE&)lpScanDst += lpbDst->bmWidthBytes;
    }
    __asm
    {
        emms
    }
    return TRUE;
}

BOOL xRender::AlphaMixStretch(PBITMAP lpbDst, PBITMAP lpbSrc, BYTE alpha)
{
    if (alpha == 0x00)
    {
        return TRUE;
    }
    else if (alpha == 0xFF)
    {
        BITMAPEX bmpSrc;
        memcpy(&bmpSrc, lpbSrc, sizeof(bmpSrc));
        bmpSrc.bmClrUsed = 0;
        bmpSrc.bmColors = NULL;
        return BlendSrcStretch[BITS_32](lpbDst, &bmpSrc);
    }

    LPDWORD lpScanDst = (LPDWORD)lpbDst->bmBits;

    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpbDst->bmWidth;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpbDst->bmHeight;
    DWORD dstWidth = lpbDst->bmWidth;
    DWORD srcByteWidth = lpbSrc->bmWidthBytes;
    DWORD srcy16 = 0;

    WORD SrcConstantAlpha = alpha + 1;
    WORD DstConstantAlpha = 256 - SrcConstantAlpha;

    for (int j = 0; j < lpbDst->bmHeight; j++)
    {
        LPXARGB lpScanSrc = (LPXARGB)((LPBYTE)lpbSrc->bmBits + srcByteWidth * (srcy16 >> 16));
        DWORD srcx16 = 0;
        LPXARGB lpPixelDst = (LPXARGB)lpScanDst;
        for (unsigned long x = 0; x < dstWidth; ++x)
        {
            xARGB src = lpScanSrc[srcx16 >> 16];

            lpPixelDst->alpha = (lpPixelDst->alpha * DstConstantAlpha + src.alpha * SrcConstantAlpha) >> 8;
            lpPixelDst->red   = (lpPixelDst->red   * DstConstantAlpha + src.red   * SrcConstantAlpha) >> 8;
            lpPixelDst->green = (lpPixelDst->green * DstConstantAlpha + src.green * SrcConstantAlpha) >> 8;
            lpPixelDst->blue  = (lpPixelDst->blue  * DstConstantAlpha + src.blue  * SrcConstantAlpha) >> 8;

            srcx16 += xrIntFloat16;
            lpPixelDst++;
        }
        srcy16 += yrIntFloat16;
        (LPBYTE&)lpScanDst += lpbDst->bmWidthBytes;
    }
    return TRUE;
}

BOOL xRender::AlphaMixStretchMMX(PBITMAP lpbDst, PBITMAP lpbSrc, BYTE alpha)
{
    if (alpha == 0x00)
    {
        return TRUE;
    }
    else if (alpha == 0xFF)
    {
        BITMAPEX bmpSrc;
        memcpy(&bmpSrc, lpbSrc, sizeof(bmpSrc));
        bmpSrc.bmClrUsed = 0;
        bmpSrc.bmColors = NULL;
        return BlendSrcStretch[BITS_32](lpbDst, &bmpSrc);
    }

    LPDWORD lpScanDst = (LPDWORD)lpbDst->bmBits;
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpbDst->bmWidth;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpbDst->bmHeight;
    DWORD dstWidth = lpbDst->bmWidth;
    DWORD srcByteWidth = lpbSrc->bmWidthBytes;
    DWORD srcy16 = 0;
    WORD SrcConstantAlpha = alpha + 1;
    WORD DstConstantAlpha = 256 - SrcConstantAlpha;

    __asm
    {
        pxor mm7, mm7
        movd mm6, SrcConstantAlpha
        punpcklwd mm6, mm6
        punpckldq mm6, mm6
        movd mm5, DstConstantAlpha
        punpcklwd mm5, mm5
        punpckldq mm5, mm5
    }
    for (int j = 0; j < lpbDst->bmHeight; j++)
    {
        LPDWORD lpScanSrc = (LPDWORD)((LPBYTE)lpbSrc->bmBits + srcByteWidth * (srcy16 >> 16));
        __asm
        {
            mov esi, lpScanSrc
            mov edi, lpScanDst
            mov edx, xrIntFloat16
            mov ecx, dstWidth
            xor eax, eax
            and ecx, (not 1)
            je ENDLOOP
            lea edi, [edi+ecx*4]
            neg ecx
WRITELOOP:
            mov ebx, eax
            shr eax, 16
            movd mm2, [esi+eax*4]
            mov eax, ebx
            lea ebx, [eax+edx]
            shr ebx, 16
            punpckldq mm2, [esi+ebx*4]
            lea eax, [eax+edx*2]
            movq mm0, [edi+ecx*4]
            movq mm1, mm0
            movq mm3, mm2
            punpcklbw mm0, mm7
            punpckhbw mm1, mm7
            punpcklbw mm2, mm7
            punpckhbw mm3, mm7
            pmullw mm0, mm5
            pmullw mm2, mm6
            pmullw mm1, mm5
            pmullw mm3, mm6
            psrlw mm0, 8
            psrlw mm1, 8
            psrlw mm2, 8
            psrlw mm3, 8
            packuswb mm0, mm7
            packuswb mm1, mm7
            packuswb mm2, mm7
            packuswb mm3, mm7
            psllq mm1, 32
            psllq mm3, 32
            pxor mm0, mm1
            pxor mm2, mm3
            paddusb	mm0, mm2
            movq qword ptr [edi+ecx*4], mm0
            add ecx, 2
            jnz WRITELOOP
ENDLOOP:
            mov ecx, dstWidth
            and ecx, 1
            je END
            shr eax, 16
            movd mm0, [edi]
            movd mm1, [esi+eax*4]
            punpcklbw mm0, mm7
            punpcklbw mm1, mm7
            pmullw mm0, mm5
            pmullw mm1, mm6
            psrlw mm0, 8
            psrlw mm1, 8
            packuswb mm0, mm7
            packuswb mm1, mm7
            paddusb mm0, mm1
            movd [edi], mm0
END:
        }
        srcy16 += yrIntFloat16;
        (LPBYTE&)lpScanDst += lpbDst->bmWidthBytes;
    }
    __asm
    {
        emms
    }
    return TRUE;
}